
/**
 * @file a52.h
 * A/52 encoder header
 */

#ifndef A52_H
#define A52_H

#include "common.h"

#include "alsophila.h"
#include "a52tab.h"
#include "bitio.h"
#include "filter.h"
#include "mdct.h"

#define ALSOPHILA_VERSION "SVN"

#define A52_MAX_CHANNELS 7

#define A52_MAX_CPL_SUBBANDS 18
#define CPL_CH 0

#define A52_NUM_BLOCKS 6

/* exponent encoding strategy */
#define EXP_REUSE 0
#define EXP_NEW   1
#define EXP_D15   1
#define EXP_D25   2
#define EXP_D45   3

#define QUALITY(csnr, fsnr) (((csnr) << 4) | (fsnr))

extern const uint8_t log2tab[256];

static inline int log2i(uint32_t v)
{
    int n = 0;
    if (v & 0xffff0000) {
        v >>= 16;
        n += 16;
    }
    if (v & 0xff00) {
        v >>= 8;
        n += 8;
    }
    n += log2tab[v];

    return n;
}

/** Delta bit allocation strategy */
typedef enum {
    DBA_REUSE = 0,
    DBA_NEW,
    DBA_NONE,
    DBA_RESERVED
} AC3DeltaStrategy;

typedef struct A52Block {
    double *input_samples[A52_MAX_CHANNELS]; /* 512 per ch */
    double mdct_coef[A52_MAX_CHANNELS][256]; /* 256 per ch */
    double transient_samples[A52_MAX_CHANNELS][512];
    int block_num;
    int blksw[A52_MAX_CHANNELS];
    int dithflag[A52_MAX_CHANNELS];
    int dynrng;
    uint8_t exp[A52_MAX_CHANNELS][256];
    int16_t psd[A52_MAX_CHANNELS][256];
    int16_t mask[A52_MAX_CHANNELS][50];
    uint8_t exp_strategy[A52_MAX_CHANNELS];
    uint8_t nexpgrps[A52_MAX_CHANNELS];
    uint8_t grp_exp[A52_MAX_CHANNELS][85];
    uint8_t bap[A52_MAX_CHANNELS][256];
    uint16_t qmant[A52_MAX_CHANNELS][256];
    int fgaincod[A52_MAX_CHANNELS];
    int write_snr;

    // coupling
    int new_cpl_coords;
    int master_cpl_coord[A52_MAX_CHANNELS];
    int cpl_coord_exp[A52_MAX_CHANNELS][A52_MAX_CPL_SUBBANDS];
    int cpl_coord_mant[A52_MAX_CHANNELS][A52_MAX_CPL_SUBBANDS];
} A52Block;

typedef struct A52BitAllocParams {
    int fscod;
    int halfratecod;
    int fgain[A52_NUM_BLOCKS][A52_MAX_CHANNELS];
    int sgain, sdecay, fdecay, dbknee, floor;
    int cplfleak, cplsleak;
} A52BitAllocParams;

typedef struct A52Frame {
    int quality;
    int bit_rate;
    int bwcode;

    double *input_audio; // allocated during init
    double *input_samples[A52_MAX_CHANNELS];
    A52Block blocks[A52_NUM_BLOCKS];
    int frame_bits;
    int exp_bits;
    int mant_bits;
    unsigned int frame_size_min; // minimum frame size
    unsigned int frame_size;     // current frame size in words
    unsigned int frmsizecod;

    // bitrate allocation control
    int sgaincod, sdecaycod, fdecaycod, dbkneecod, floorcod;
    A52BitAllocParams bit_alloc;
    int csnroffst;
    int fsnroffst;
    int start_freq[A52_MAX_CHANNELS];
    int end_freq[A52_MAX_CHANNELS];
    int ncoefs[A52_MAX_CHANNELS];
    int expstr_set[A52_MAX_CHANNELS];
    uint8_t rematflg[4];
    int num_rematrixing_bands;

    // coupling
    int cpl_in_use;
    int channel_in_cpl[A52_MAX_CHANNELS];
    int cpl_coord_interval;
    int cpl_start_subband;
    int cpl_end_subband;
    int num_cpl_subbands;
    int cpl_band_struct[A52_MAX_CPL_SUBBANDS];
    int cpl_band_size[A52_MAX_CPL_SUBBANDS];
    int num_cpl_bands;
} A52Frame;

typedef struct A52Context {
    void (*fmt_convert_from_src)(double *dest[A52_MAX_CHANNELS],
                                 const void *vsrc, int nch, int n);

    A52Frame frame;
    BitWriter bw;
    uint8_t frame_buffer[A52_MAX_CODED_FRAME_SIZE];

    AlsophilaEncParams params;
    AlsophilaMetadata meta;

    uint32_t bit_cnt;
    uint32_t sample_cnt;

    int n_channels;
    int n_all_channels;
    int acmod;
    int lfe;
    int lfe_channel;
    int sample_rate;
    int halfratecod;
    int bsid;
    int fscod;
    int bsmod;
    int target_bitrate;
    int frmsizecod;
    int fixed_bwcode;

    FilterContext bs_filter[A52_MAX_CHANNELS];
    FilterContext dc_filter[A52_MAX_CHANNELS];
    FilterContext bw_filter[A52_MAX_CHANNELS];
    FilterContext lfe_filter;

    int last_quality;
    int last_samples_count;
    double last_transient_samples[A52_MAX_CHANNELS][256];

    int header_size;
    uint8_t header_data[14];

    MDCTContext mdct_ctx_512;
    MDCTContext mdct_ctx_256;
} A52Context;


extern int a52_output_audio_blocks(A52Context *ctx, int write);

#endif /* A52_H */
